package com.skyline.energy.provider.spring;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.InterruptibleBatchPreparedStatementSetter;
import org.springframework.jdbc.core.ParameterDisposer;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.RowMapperResultSetExtractor;
import org.springframework.jdbc.support.JdbcUtils;

import com.skyline.energy.dataaccess.jdbc.KeyHolder;

public final class PreparedStatementCallbackCreator {
	private PreparedStatementCallbackCreator() {

	}

	public static PreparedStatementCallback<int[]> createPreparedStatementCallback(
			final BatchPreparedStatementSetter pss, final KeyHolder generatedKeyHolder) {
		PreparedStatementCallback<int[]> callback = new PreparedStatementCallback<int[]>() {

			@Override
			public int[] doInPreparedStatement(PreparedStatement ps) throws SQLException {
				try {
					int batchSize = pss.getBatchSize();
					InterruptibleBatchPreparedStatementSetter ipss = null;
					if (pss instanceof InterruptibleBatchPreparedStatementSetter) {
						ipss = (InterruptibleBatchPreparedStatementSetter) pss;
					}
					if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
						for (int i = 0; i < batchSize; i++) {
							pss.setValues(ps, i);
							if (ipss != null && ipss.isBatchExhausted(i)) {
								break;
							}
							ps.addBatch();
						}
						int[] rows = ps.executeBatch();
						return processResult(ps, rows, true);
					} else {
						List<Integer> rowsAffected = new ArrayList<Integer>();
						int[] rows = new int[1];
						for (int i = 0; i < batchSize; i++) {
							pss.setValues(ps, i);
							if (ipss != null && ipss.isBatchExhausted(i)) {
								break;
							}
							int rowAffected = ps.executeUpdate();
							rows[0] = rowAffected;
							rowsAffected.add(rowAffected);
							processResult(ps, rows, (i == 0)); // 第一次清空KeyHolder
						}
						return processAffectedArray(rowsAffected);
					}
				} finally {
					if (pss instanceof ParameterDisposer) {
						((ParameterDisposer) pss).cleanupParameters();
					}
				}
			}

			private int[] processAffectedArray(List<Integer> rowsAffected) {
				int[] rowsAffectedArray = new int[rowsAffected.size()];
				for (int i = 0; i < rowsAffectedArray.length; i++) {
					rowsAffectedArray[i] = rowsAffected.get(i);
				}
				return rowsAffectedArray;
			}

			private int[] processResult(PreparedStatement ps, int[] rows, boolean clearKeys) throws SQLException {
				if (generatedKeyHolder != null) {
					List<Map<String, Object>> generatedKeys = generatedKeyHolder.getKeyList();
					if (clearKeys) {
						generatedKeys.clear();
					}
					ResultSet keys = ps.getGeneratedKeys();
					if (keys != null) {
						try {
							RowMapperResultSetExtractor<Map<String, Object>> rse = 
								new RowMapperResultSetExtractor<Map<String, Object>>(
									new ColumnMapRowMapper(), rows.length);
							generatedKeys.addAll(rse.extractData(keys));
						} finally {
							JdbcUtils.closeResultSet(keys);
						}
					}
				}
				return rows;
			}

		};

		return callback;
	}
}
